﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace LigerMVC.Common
{
    internal class QueryExpressionParser<T>
    {
        public Expression<Func<T, bool>> Parse(IEnumerable<QueryCondition> conditions, bool isAlso = true)
        {
            QuerySimpleDescriptor.PrepareCondition<T>(conditions); 

            var query = ParseInternal(conditions, isAlso);

            return Expression.Lambda<Func<T, bool>>(query, parameter);
        }

        public Expression<Func<T, bool>> Parse(FilterGroup filter)
        {

            var query = ParseInternal(filter); 

            return Expression.Lambda<Func<T, bool>>(query, parameter);
        }
         

        ParameterExpression parameter = Expression.Parameter(typeof(T));


        private Expression ParseInternal(FilterGroup filter)
        { 
            Expression express = null;
            var isAlso = filter.op == "and"; 
            if (filter == null)
            {
                return Expression.Constant(true, typeof(bool));
            } 
            IList<Expression> expressions = new List<Expression>();
            if(filter.rules != null)
            {
                foreach(var rule in filter.rules)
                {
                    var condition = QuerySimpleDescriptor.ConvertToCondition(rule);
                    QuerySimpleDescriptor.PrepareCondition<T>(condition);
                    expressions.Add(ParseSingle(condition));
                }
            }
            if(filter.groups != null)
            {
                foreach(var group in filter.groups)
                {
                    expressions.Add(ParseInternal(group));
                }
            }
            if(!expressions.Any())
            {
                 return Expression.Constant(true, typeof(bool));
            }
            foreach (var exp in expressions)
            {
                //第一个表达式
                if (express == null)
                {
                    express = exp;
                }
                //拿第N个跟第N-1个合并
                else
                {
                    if (isAlso)
                    {
                        express = Expression.AndAlso(express, exp);
                    }
                    else
                    {
                        express = Expression.OrElse(express, exp);
                    }
                }
            }
            return express;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="conditions"></param>
        /// <param name="isAlso"></param>
        /// <param name="additional">额外的条件</param>
        /// <returns></returns>
        private Expression ParseInternal(IEnumerable<QueryCondition> conditions, bool isAlso = true)
        {
            Expression express = null;
            if (conditions == null || conditions.Count() == 0)
            {
                express = Expression.Constant(true,typeof(bool));
            }
            else if (conditions.Count() == 1)
            {
                express = ParseSingle(conditions.First());
            }
            else
            {
                Expression a = ParseSingle(conditions.First());
                Expression b = ParseInternal(conditions.Skip(1), isAlso);
                if (isAlso)
                {
                    express = Expression.AndAlso(a, b);
                }
                else
                {
                    express = Expression.OrElse(a, b);
                }
            }
            return express;
        }

        private Expression  ParseSingle(QueryCondition condition)
        {
            ParameterExpression p = parameter;
            Expression key = ParseKey(p,condition);  
            Expression value = ParseValue(condition, TypeHelper.IsNullableType(key.Type)); 
            Expression method = ParseMethod(key, value, condition);
            return method;
        }

        private Expression ParseKey(ParameterExpression parameter,QueryCondition condition)
        {
            Expression p = parameter;
            condition.Key = condition.Key.Replace("..", ".");
            var properties = condition.Key.Split('.');
            foreach (var property in properties)            
                p=Expression.Property(p, property);            
            return p;
        }

        private Expression ParseValue(QueryCondition condition,bool nullable = false)
        {    
            Expression value = null;
            Type type = null;
            if (nullable)
            {
                if (condition.Value == null)
                {

                }
                else if (condition.Value.GetType() == typeof(int))
                {
                    type = typeof(Nullable<int>);
                }
                else if (condition.Value.GetType() == typeof(double))
                {
                    type = typeof(Nullable<double>);
                } 
                else if (condition.Value.GetType() == typeof(bool))
                {
                    type = typeof(Nullable<bool>);
                } 
                else if (condition.Value.GetType() == typeof(byte))
                {
                    type = typeof(Nullable<byte>);
                } 
                else if (condition.Value.GetType() == typeof(long))
                {
                    type = typeof(Nullable<long>);
                } 
                else if (condition.Value.GetType() == typeof(double))
                {
                    type = typeof(Nullable<double>);
                }
                else if (condition.Value.GetType() == typeof(DateTime))
                {
                    type = typeof(Nullable<DateTime>);
                }
                else if (condition.Value.GetType() == typeof(decimal))
                {
                    type = typeof(Nullable<decimal>);
                }
            }
            if (type != null)
            {
                value = Expression.Constant(condition.Value, type);
            }
            else
            {
                value = Expression.Constant(condition.Value);
            }
            return value;
        }

        private Expression ParseMethod(Expression key, Expression value, QueryCondition condition)
        {
            switch (condition.Operator)
            {
                case QueryOperator.Contains:
                case QueryOperator.Like:
                    return Expression.Call(key, typeof(string).GetMethod("Contains"), value);
                case QueryOperator.Equal:
                    return Expression.Equal(key, value);
                case QueryOperator.NotEqual:
                    return Expression.NotEqual(key, value);
                case QueryOperator.GreaterThan:
                    return Expression.GreaterThan(key, value);
                case QueryOperator.GreaterThanOrEqual:
                    return Expression.GreaterThanOrEqual(key, value);
                case QueryOperator.LessThan:
                    return Expression.LessThan(key, value);
                case QueryOperator.LessThanOrEqual:
                    return Expression.LessThanOrEqual(key, value);
                default:
                    throw new NotImplementedException();   //Operator IN is difficult to implenment. Wait a sec.....                
            }
          
        }        
    }
}
